package internal

import (
	"context"
	"gameserver/messages"
	"gameserver/server"
	"sync"

	"gitee.com/night-tc/gobige/global"
	"gitee.com/night-tc/gobige/logger"
	"gitee.com/night-tc/gobige/msgdef"
	"gitee.com/night-tc/gobige/serverMgr"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/credentials/insecure"
	"google.golang.org/grpc/status"
)

/*
链接linker服务器的连接对象
*/

type LinkerSrv struct {
	GroupID uint32 // 服务器组ID
	// grpc连接对象
	// 这里的连接对象是一个map, key是服务器ID
	li map[uint64]messages.GrpcLinkerClient

	locker sync.RWMutex // 读写锁，用于保护li的并发访问
}

func NewLinkerSrv(groupID uint32) *LinkerSrv {
	result := new(LinkerSrv)
	result.GroupID = groupID
	result.li = make(map[uint64]messages.GrpcLinkerClient)

	// 初始化连接对象
	// 这里可以添加一些初始化逻辑，比如连接到特定的服务器等

	return result
}

/*
CallRouterSrvReqByPID 通过进程ID(PID)和服务类型(srvtype)将路由消息转发到其他服务器

参数:
  - ctx: 上下文对象，用于控制请求的生命周期
  - msg: 需要转发的路由消息，必须实现IMsg接口
  - pid: 目标服务器的进程ID
  - srvtype: 目标服务器的服务类型

返回值:
  - out: 目标服务器返回的消息，实现IMsg接口
  - err: 错误信息，成功时为nil

流程说明:
 1. 将消息序列化为字节数据
 2. 构造GrpcSrvReq请求对象
 3. 调用DoRouterSrvReq发送请求
 4. 将响应数据反序列化为消息对象
*/
func (this *LinkerSrv) CallRouterSrvReqByPID(ctx context.Context, msg msgdef.IMsg, pid uint64, srvtype uint32) (out msgdef.IMsg, err error) {
	// 序列化消息数据
	data, err := msg.Marshal()
	if err != nil {
		return nil, status.Errorf(codes.Internal, "failed to marshal message: %v", err)
	}

	// 构造gRPC请求对象
	in := &messages.GrpcSrvReq{
		PID:     pid,
		SrvType: srvtype,
		MsgName: msg.Name(),
		MsgData: data,
		OrderID: srvinst.GetNewEID(), // 生成唯一请求ID
		Prefix:  "",
	}

	// 发送请求并获取响应
	ret, err := this.doRouterSrvReq(ctx, in)
	logger.Debugf(logger.LogKey_Default, "RouterSrvReqByPID Ret:%v,err:%v", ret, err)
	if err != nil {
		return nil, err
	}

	// 将响应数据反序列化为消息对象
	out, err = msgdef.GetTypeMgr().NewMsgByName(ret.MsgName, ret.MsgData)
	return out, err
}

// 路由消息到其他服务器
func (this *LinkerSrv) doRouterSrvReq(ctx context.Context, in *messages.GrpcSrvReq) (out *messages.GrpcSrvRet, err error) {
	var lastErr error
	defer func() {
		if lastErr != nil {
			this.checkLinkerClient() // 检查并清理无效的客户端连接
		}
	}()
	this.locker.RLock()
	defer this.locker.RUnlock()

	for _, client := range this.li {
		out, err = client.DoRouterSrvReq(ctx, in)
		if err != nil {
			// 如果是连接错误，记录错误并尝试下一个连接
			if status.Code(err) == codes.Unavailable {
				lastErr = err
				continue
			}
			// 其他错误直接返回
			return nil, err
		}
		return out, nil
	}

	// 所有连接都尝试过且都失败了，返回最后一个错误
	if lastErr != nil {
		return nil, lastErr
	}
	return nil, status.Errorf(codes.NotFound, "server not found for group %d", this.GroupID)
}

// AddLinkerClient 添加一个Linker服务器的客户端连接
// 这个方法会在服务器信息变更时调用，通常是新服务器上线时
func (this *LinkerSrv) AddLinkerClient(sinfo serverMgr.IServerInfo) {
	this.locker.Lock()
	defer this.locker.Unlock()
	if _, ok := this.li[sinfo.GetServerID()]; ok {
		return
	}
	cc, err := grpc.NewClient(sinfo.GetListenAdd(global.ListenType_Http),
		grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		logger.Errorf("failed to create grpc client: %v", err)
		return
	}

	client := messages.NewGrpcLinkerClient(cc)
	this.li[sinfo.GetServerID()] = client
	logger.Infof("Added Linker client for server ID %d in group %d", sinfo.GetServerID(), this.GroupID)
}

// DelLinkerClient 删除一个Linker服务器的客户端连接
// 这个方法会在服务器信息变更时调用，通常是服务器下线时
func (this *LinkerSrv) DelLinkerClient(sinfo serverMgr.IServerInfo) {
	this.locker.Lock()
	defer this.locker.Unlock()
	delete(this.li, sinfo.GetServerID())
	logger.Infof("Deleted Linker client for server ID %d in group %d", sinfo.GetServerID(), this.GroupID)
}

func (this *LinkerSrv) checkLinkerClient() {
	this.locker.Lock()
	defer this.locker.Unlock()
	for id := range this.li {
		_, err := GetSrvinst().GetDirMgr().GetServerById(this.GroupID, server.ServerType_Linker, id)
		if err != nil {
			logger.Infof("failed to get server info for ID %d: %v", id, err)
			delete(this.li, id) // 删除无效的客户端连接
			continue
		}
	}
}

// LinkerSrvMgr 管理所有的LinkerSrv实例
// 这个管理器负责根据GroupID获取或创建LinkerSrv实例
type LinkerSrvMgr struct {
	Linkers map[uint32]*LinkerSrv // key: GroupID
	locker  sync.RWMutex          // 读写锁，用于保护Linkers的并发访问

}

func NewLinkerSrvMgr() *LinkerSrvMgr {
	result := new(LinkerSrvMgr)
	result.Linkers = make(map[uint32]*LinkerSrv)
	return result
}

func (this *LinkerSrvMgr) GetLinkerSrv(groupID uint32) *LinkerSrv {
	result := this.getLinkerSrv(groupID)
	if result != nil {
		return result
	} else {
		return this.addLinkerSrv(groupID)
	}

}

// GetLinkerSrv 获取指定GroupID的LinkerSrv实例
func (this *LinkerSrvMgr) getLinkerSrv(groupID uint32) *LinkerSrv {
	this.locker.RLock()
	defer this.locker.RUnlock()
	return this.Linkers[groupID]
}

func (this *LinkerSrvMgr) addLinkerSrv(groupid uint32) *LinkerSrv {
	this.locker.Lock()
	defer this.locker.Unlock()
	result, ok := this.Linkers[groupid]
	if ok {
		return result
	} else {
		result = NewLinkerSrv(groupid)
		this.Linkers[groupid] = result
		return result
	}

}
